%%--- coding:utf-8 ---
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% File Name: mc_worker_api
%%% Created on : 2024/4/16 21:19
%%% @author Gaylen 252323463@qq.com
%%% @copyright (C) 2024, freedom
%%% @doc
%%%
%%% @end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-module(mc_worker_api).
-author("Gaylen").
-include("mongodb_driver.hrl").

%% API
-export([
    connect/0,
    connect/1,
    disconnect/1,
%%    get_version/2,

    command/3,
    command/4,

    insert/4,

    update/5,
    update/7,

    delete_all/4,
    delete_one/4,
    delete_limit/5,

    find_one/4,
    find_one/5,

    find/4,
    find/5,
    find/6,
    find/8,
    find/9,

    find_and_modify/5,
    find_and_modify/7,

    count/4,
    count/6,

    ensure_index/5,
    ensure_index/6,

    ping/2,
    is_master/2
]).

%% @doc shortcut for connect/1 with default params.
connect() ->
    connect([]).

%% @doc Make one connection to server, return its pid
-spec connect(args()) -> {ok, pid()}.
%% Options [{host, "127.0.0.1"}, ...]
%%   host 数据库ip,默认"127.0.0.1"
%%   port 数据库端口,默认27017
%%   database 访问的数据库名,默认<<"test">>
%%   user  <<"用户名">>
%%   password <<"密码">>
%%   auth_source 授权源，一般为 <<"admin">>
%%   ssl  是否使用ssl连接, true|false
%%   ssl_opts SSL参数
%%   timeout 连接超时,单位毫秒,默认infinity
%%   register 注册进程别名
connect(Args) ->  % TODO args as map
    {ok, Connection} = mc_worker:start_link(Args),
    User = mc_utils:get_value(user, Args),
    Password = mc_utils:get_value(password, Args),
    case (User /= undefined) and (Password /= undefined) of
        true ->
            AuthSource = mc_utils:get_value(auth_source, Args, <<"admin">>),
%%            Version = get_version(Connection, AuthSource),
            mc_auth_logic:auth(Connection, AuthSource, User, Password);
        false -> ok
    end,
    {ok, Connection}.

%%%% 获取mongo服务的版本号.
%%-spec get_version(pid(), database()) -> float().
%%get_version(Connection, Database) ->
%%    {true, #{<<"version">> := Version}} = command(Connection, Database, {<<"buildinfo">>, 1}),
%%    {VFloat, _} = string:to_float(binary_to_list(Version)),
%%    VFloat.

%% 断开mongo连接
-spec disconnect(pid()) -> ok.
disconnect(Connection) ->
    mc_worker:disconnect(Connection).

%%%%%%%%%% command %%%%%%%%%%%%%%%%%%%%%%%%
%% @doc Execute given MongoDB command on specific database and return its result.
-spec command(pid(), database(), selector()) -> {boolean(), map()}.
command(Connection, Database, Command) ->
    mc_worker_api_logic:database_command(Connection, Database, Command).

command(Connection, Database, Command, IsSlaveOk) ->
    mc_worker_api_logic:database_command(Connection, Database, Command, IsSlaveOk).

%%%%%%%%%% insert %%%%%%%%%%%%%%%%%%%%%%%%
-spec insert(pid(), database(), collection(), [bson:document()] | bson:document()) -> {boolean(), map()}.
insert(Connection, Database, Table, Docs) ->
    mc_worker_api_logic:database_insert(Connection, Database, Table, Docs).

%%%%%%%%%% update %%%%%%%%%%%%%%%%%%%%%%%%
-spec update(pid(), database(), collection(), selector(), map() | bson:document()) -> {boolean(), map()}.
update(Connection, Database, Table, Selector, Doc) ->
    update(Connection, Database, Table, Selector, Doc, false, false).

-spec update(pid(), database(), collection(), selector(), map() | bson:document(), boolean(), boolean()) -> {boolean(), map()}.
update(Connection, Database, Table, Selector, Doc, IsUpInsert, IsMultiUpdate) ->
    mc_worker_api_logic:database_update(Connection, Database, Table, Selector, Doc, IsUpInsert, IsMultiUpdate).

%%%%%%%%%% delete %%%%%%%%%%%%%%%%%%%%%%%%
-spec delete_all(pid(), database(), collection(), selector()) -> {boolean(), map()}.
delete_all(Connection, Database, Table, Selector) ->
    delete_limit(Connection, Database, Table, Selector, 0).
-spec delete_one(pid(), database(), collection(), selector()) -> {boolean(), map()}.
delete_one(Connection, Database, Table, Selector) ->
    delete_limit(Connection, Database, Table, Selector, 1).
-spec delete_limit(pid(), database(), collection(), selector(), integer()) -> {boolean(), map()}.
delete_limit(Connection, Database, Table, Selector, Limit) ->
    mc_worker_api_logic:database_delete(Connection, Database, Table, Selector, Limit).

%%%%%%%%%% find_one %%%%%%%%%%%%%%%%%%%%%%%%
-spec find_one(pid(), database(), collection(), selector()) -> {boolean(), undefined | map()}.
find_one(Connection, Database, Table, Selector) ->
    find_one(Connection, Database, Table, Selector, #{}).
-spec find_one(pid(), database(), collection(), selector(), map()) -> {boolean(), undefined | map()}.
find_one(Connection, Database, Table, Selector, Projector) ->
    mc_worker_api_logic:database_find_one(Connection, Database, Table, Selector, Projector).

%%%%%%%%%% find %%%%%%%%%%%%%%%%%%%%%%%%
-spec find(pid(), database(), collection(), selector()) -> {ok, [map()], cursor()} | {error, map()}.
find(Connection, Database, Table, Selector) ->
    find(Connection, Database, Table, Selector, #{}).
-spec find(pid(), database(), collection(), selector(), map()) -> {ok, [map()], cursor() | undefined} | {error, map()}.
find(Connection, Database, Table, Selector, SortDoc) ->
    find(Connection, Database, Table, Selector, SortDoc, #{}).
-spec find(pid(), database(), collection(), selector(), map(), map()) -> {ok, [map()], cursor() | undefined} | {error, map()}.
find(Connection, Database, Table, Selector, SortDoc, Projector) ->
    find(Connection, Database, Table, Selector, SortDoc, Projector, 0, 0).
-spec find(pid(), database(), collection(), selector(), map(), map(), integer(), integer()) -> {ok, [map()], cursor() | undefined} | {error, map()}.
find(Connection, Database, Table, Selector, SortDoc, Projector, Skip, Limit) ->
    find(Connection, Database, Table, Selector, SortDoc, Projector, Skip, Limit, false).
-spec find(pid(), database(), collection(), selector(), map(), map(), integer(), integer(), boolean()) -> {ok, [map()], cursor() | undefined} | {error, map()}.
find(Connection, Database, Table, Selector, SortDoc, Projector, Skip, Limit, IsSlave) ->
    mc_worker_api_logic:database_find(Connection, Database, Table, Selector, SortDoc, Projector, Skip, Limit, IsSlave).

%%%%%%%%%% find_and_modify %%%%%%%%%%%%%%%%%%%%%%%%
-spec find_and_modify(pid(), database(), collection(), selector(), map() | bson:document()) -> {boolean(), map()}.
find_and_modify(Connection, Database, Table, Selector, UpdateDoc) ->
    find_and_modify(Connection, Database, Table, Selector, UpdateDoc, #{}, true).
-spec find_and_modify(pid(), database(), collection(), selector(), map() | bson:document(), map() | bson:document(), boolean()) -> {boolean(), map()}.
find_and_modify(Connection, Database, Table, Selector, UpdateDoc, Projector, IsUpInsert) ->
    mc_worker_api_logic:database_find_and_modify(Connection, Database, Table, Selector, UpdateDoc, Projector, IsUpInsert).

%%%%%%%%%% count %%%%%%%%%%%%%%%%%%%%%%%%
-spec count(pid(), database(), collection(), selector()) -> {boolean(), integer()}.
count(Connection, Database, Table, Selector) ->
    count(Connection, Database, Table, Selector, 0, 0).
-spec count(pid(), database(), collection(), selector(), integer(), integer()) -> {boolean(), integer()}.
count(Connection, Database, Table, Selector, Limit, Skip) ->
    CmdDoc = {
        <<"count">>, Table,
        <<"query">>, Selector,
        <<"limit">>, Limit,
        <<"skip">>, Skip
    },
    case command(Connection, Database, CmdDoc) of
        {true, Doc} ->
            {true, maps:get(<<"n">>, Doc, 0)};
        _ ->
            {false, 0}
    end.

%%%%%%%%%% ensure_index %%%%%%%%%%%%%%%%%%%%%%%%
%% IndexSpecs: {key, -1|1, ...}
-spec ensure_index(pid(), database(), collection(), bson:document(), boolean()) -> {boolean(), map()}.
ensure_index(Connection, Database, Table, IndexSpecs, Unique) ->
    mc_worker_api_logic:database_ensure_index(Connection, Database, Table, IndexSpecs, Unique, false).

-spec ensure_index(pid(), database(), collection(), bson:document(), boolean(), boolean()) -> {boolean(), map()}.
ensure_index(Connection, Database, Table, IndexSpecs, Unique, DropDups) ->
    mc_worker_api_logic:database_ensure_index(Connection, Database, Table, IndexSpecs, Unique, DropDups).

-spec ping(pid(), database()) -> ok | {error, bson:document()}.
ping(Connection, Database) ->
    mc_worker_api_logic:database_ping(Connection, Database).

-spec is_master(pid(), database()) -> boolean().
is_master(Connection, Database) ->
    mc_worker_api_logic:database_is_master(Connection, Database).

